//! 地址转换

use core::mem::size_of;

use super::kalloc::Page;
use crate::println;

cfg_if::cfg_if! {
    if #[cfg(target_arch = "aarch64")] {
        mod aarch64;
        use aarch64::*;
    } else {
        mod dummy;
        use dummy::*;
    }
}

/// 内核起始虚拟地址
#[inline]
pub fn start_text() -> usize {
    arch_start_text()
}

/// 内核结束虚拟地址
#[inline]
pub fn end_bss() -> usize {
    arch_end_bss()
}

// init task 栈基址
#[inline]
pub(crate) fn init_stack_base() -> usize {
    arch_init_stack_base()
}

/// 页大小
pub const PAGE_SIZE: usize = ARCH_PAGE_SIZE;
/// 页偏移
pub const PAGE_SHIFT: usize = ARCH_PAGE_SHIFT;
/// 页掩码
pub const PAGE_MASK: usize = !(PAGE_SIZE - 1);

// 内核代码的起始偏移地址, 可实现物理地址和内核地址的互相转换
const KIMAGE_OFFSET: usize = ARCH_KIMAGE_OFFSET;

/// 物理地址转换为内核地址
pub const fn phys_to_kimg(paddr: usize) -> usize {
    paddr + KIMAGE_OFFSET
}

/// 内核地址转换为物理地址
pub const fn kimg_to_phys(kaddr: usize) -> usize {
    kaddr - KIMAGE_OFFSET
}

// 内核 io 起始偏移地址, 可实现物理地址和 io 地址的互相转换
const KIO_OFFSET: usize = ARCH_KIO_OFFSET;

/// 物理地址转换为 io 地址
pub const fn phys_to_io(paddr: usize) -> usize {
    paddr + KIO_OFFSET
}

/// io 地址转换为物理地址
pub const fn io_to_phys(kio: usize) -> usize {
    kio - KIO_OFFSET
}

// 线性内存起始偏移地址, 可实现物理地址和线性地址的互相转换
const KMEM_OFFSET: usize = ARCH_KMEM_OFFSET;

/// 物理地址转换为线性地址
pub const fn phys_to_virt(paddr: usize) -> usize {
    paddr + KMEM_OFFSET
}

/// 线性地址转换为物理地址
pub const fn virt_to_phys(vaddr: usize) -> usize {
    vaddr - KMEM_OFFSET
}

/// 线性地址转换为页帧
pub fn virt_to_pfn(vaddr: usize) -> usize {
    phys_to_pfn(virt_to_phys(vaddr))
}

/// 线性地址转换为页结构
#[inline(always)]
pub fn virt_to_page(vaddr: usize) -> &'static Page {
    phys_to_page(virt_to_phys(vaddr))
}

/// 线性地址转换为页结构(可变的)
#[inline(always)]
pub fn virt_to_page_mut(vaddr: usize) -> &'static mut Page {
    phys_to_page_mut(virt_to_phys(vaddr))
}

extern "Rust" {
    // 物理地址范围(start, size)
    // 注意:
    // 这里实现只能支持连续的内存地址块, 中间不能存在内从空洞
    static __MEMRANGE: (usize, usize);
}

/// 内存起始物理地址
#[inline(always)]
pub fn memstart() -> usize {
    unsafe { __MEMRANGE.0 }
}

/// 内存大小
#[inline(always)]
pub fn memsize() -> usize {
    unsafe { __MEMRANGE.1 }
}

/// 内存结束物理地址
#[inline(always)]
pub fn memend() -> usize {
    memstart() + memsize()
}

/// 物理页转换为页帧
pub fn phys_to_pfn(phys: usize) -> usize {
    (phys - memstart()) >> PAGE_SHIFT
}

/// 物理页转换为页帧并向上 `PAGE_SIZE` 对齐
pub fn phys_to_pfn_up(mut phys: usize) -> usize {
    phys += PAGE_SIZE - 1;
    (phys - memstart()) >> PAGE_SHIFT
}

/// 页帧转换为物理地址
pub fn pfn_to_phys(pfn: usize) -> usize {
    (pfn << PAGE_SHIFT) + memstart()
}

// 页结构地址偏移, 可用于通过其他地址快速找到对应的页结构
const KPAGE_OFFSET: usize = ARCH_KPAGE_OFFSET;
// 页结构大小
const KPAGE_SIZE: usize = size_of::<Page>();
// 页结构总数量
#[inline(always)]
fn kpage_count() -> usize {
    memsize() / PAGE_SIZE
}
// 页结构占用总内存量
#[inline(always)]
fn kpage_mem() -> usize {
    KPAGE_SIZE * kpage_count()
}

/// 页结构起始地址
///
/// 实际的页结构起始地址
/// 通过定义为物理地址高位位置到内存结束区域为页结构存储空间
pub fn kpage_start() -> usize {
    KPAGE_OFFSET + kpage_start_phys()
}

/// 页结构结束地址
pub fn kpage_end() -> usize {
    kpage_start() + kpage_mem()
}

/// 页结构起始物理地址
pub fn kpage_start_phys() -> usize {
    memend() - kpage_mem()
}

/// 物理地址转换为一个页结构
///
/// 注意:
///
/// 这里的设计需要保证内存是连续的, 没有空洞内存,
/// 因为 KPAGE 是连续内存映射的.
pub fn phys_to_page(paddr: usize) -> &'static Page {
    let index = phys_to_pfn(paddr);
    unsafe { &*(kpage_start() as *const Page).add(index) }
}

/// 物理地址转换为一个页结构(可修改页结构)
pub fn phys_to_page_mut(paddr: usize) -> &'static mut Page {
    let index = (paddr - memstart()) >> PAGE_SHIFT;
    unsafe { &mut *(kpage_start() as *mut Page).add(index) }
}

/// 页结构转换为物理地址
#[inline(always)]
pub fn page_to_phys(page: &Page) -> usize {
    let p = page as *const Page as usize;
    (((p - kpage_start()) / KPAGE_SIZE) << PAGE_SHIFT) + memstart()
}

/// 页结构转换为页帧
#[inline(always)]
pub fn page_to_pfn(page: &Page) -> usize {
    phys_to_pfn(page_to_phys(page))
}

/// 页结构转换为线性地址
#[inline(always)]
pub fn page_to_virt(page: &Page) -> usize {
    phys_to_virt(page_to_phys(page))
}

/// dump 内存分布
pub fn mem_dump() {
    println!("phys: [{:#018x}-{:#018x}]", memstart(), memend() - 1);
    println!("virt: [{:#018x}-{:#018x}]", phys_to_virt(memstart()), phys_to_virt(memend()) - 1);
    println!("page: [{:#018x}-{:#018x}]", kpage_start(), kpage_end());
    println!("kimg: [{:#018x}-{:#018x}]", start_text(), end_bss() - 1);
}

#[derive(Clone, Copy)]
struct MemBlockRange {
    start: usize,
    size: usize,
}

// 定义有 16 块, 目前足够
const MEM_RANG_MAX: usize = 16;

struct MemBlock {
    index: usize,
    range: [MemBlockRange; MEM_RANG_MAX],
}

// 定义一个全局的可用内存范围, 最多可以设置 MEM_RANG_MAX 块内存区域
static mut MEMBLOCK_RANGE: MemBlock =
    MemBlock { index: 0, range: [MemBlockRange { start: 0, size: 0 }; MEM_RANG_MAX] };

/// 添加一块内存域
///
/// 注意: 添加内存域不能与已添加的内存域交叉并且不能添加超过 `MEM_RANG_MAX`, 否则将会断言失败
///
/// # Panics
/// 只能由主核启动时添加, 否则存在并发问题, 并且内存不能交叉
pub fn memblock_add(start: usize, size: usize) {
    let end = start + size;
    unsafe {
        let index = MEMBLOCK_RANGE.index;
        assert!(index < MEM_RANG_MAX);
        for i in 0..index {
            let mem_end = MEMBLOCK_RANGE.range[i].start + MEMBLOCK_RANGE.range[i].size;
            assert!(end <= MEMBLOCK_RANGE.range[i].start || mem_end <= start);
        }
        MEMBLOCK_RANGE.range[index].start = start;
        MEMBLOCK_RANGE.range[index].size = size;
        MEMBLOCK_RANGE.index += 1;
    }
}

/// 判断页帧是否是一个可用的页帧
///
/// 判断标准则是根据页帧是否在添加的内存域为标准
pub fn pfn_valid(pfn: usize) -> bool {
    unsafe {
        let index = MEMBLOCK_RANGE.index;
        for i in 0..index {
            let start = MEMBLOCK_RANGE.range[i].start;
            let end = start + MEMBLOCK_RANGE.range[i].size;
            let start_pfn = phys_to_pfn(start);
            let end_pfn = phys_to_pfn(end);
            if pfn >= start_pfn && pfn < end_pfn {
                return true;
            }
        }
    }
    false
}

/// 遍历内存域
pub fn memblock_foreach<F>(f: F)
where
    F: Fn(usize, usize),
{
    unsafe {
        let index = MEMBLOCK_RANGE.index;
        for i in 0..index {
            let start = MEMBLOCK_RANGE.range[i].start;
            let size = MEMBLOCK_RANGE.range[i].size;
            f(start, size);
        }
    }
}

/// 全局可用内存域设置
///
/// f: start, size, `is_free`
pub fn mem_setup<F>(f: F)
where
    F: Fn(usize, usize, bool),
{
    let end = pfn_to_phys(phys_to_pfn(kimg_to_phys(start_text())));
    memblock_add(memstart(), end - memstart());
    f(memstart(), end - memstart(), true);

    let start = pfn_to_phys(phys_to_pfn_up(kimg_to_phys(end_bss())));
    f(end, start - end, false);
    let end = kpage_start_phys();
    memblock_add(start, end - start);
    f(start, end - start, true);
    f(end, memend() - end, false);
}
